package com.xiaomaoguai.fcp.pre.kepler.router.rpc.client.okhttp;

import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.beans.BeanMap;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.constants.HttpConstants;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.enums.HttpMethod;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.exception.HttpRequestException;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.Response;

public final class RequestBuilder {

	HttpMethod method = HttpMethod.GET;
	String url;
	String contentType = HttpConstants.HEADER_CONTENT_TYPE_FORM;
	Map<String, String> headers = Maps.newHashMap();
	boolean keepAlive = true;
	private OkHttpClient okHttpClient;

	public RequestBuilder() {
	}

	public RequestBuilder(Request request) {
		method = request.method();
		headers = request.headers();
		url = request.url();
	}

	/**
	 * 设置http method
	 * @see com.xiaomaoguai.fcp.pre.kepler.router.rpc.enums.HttpMethod
	 * @param method
	 * @return
	 */
	public RequestBuilder method(HttpMethod method) {
		this.method = Objects.requireNonNull(method);
		return this;
	}

	/**
	 * 单独设置http contentType
	 * @see com.xiaomaoguai.fcp.pre.kepler.router.rpc.constants.HttpConstants
	 * @param method
	 * @return
	 */
	public RequestBuilder contentType(String contentType) {
		this.contentType = contentType;
		headers.put(HttpConstants.HEADER_CONTENT_TYPE, contentType);
		return this;
	}

	public RequestBuilder url(String url) {
		this.url = url;
		return this;
	}

	public RequestBuilder okHttpClient(OkHttpClient okHttpClient) {
		this.okHttpClient = Objects.requireNonNull(okHttpClient);
		return this;
	}

	/**
	 * 设置单个头信息
	 */
	public RequestBuilder headers(Header header) {
		this.headers.put(header.name(), header.value());
		return this;
	}

	public RequestBuilder headers(Map<String, String> headers) {
		this.headers.putAll(headers);
		return this;
	}

	/**
	 * 设置多个头信息
	 */
	public RequestBuilder headers(List<Header> headers) {
		headers.forEach((header) -> {
			this.headers.put(header.name(), header.value());
		});
		return this;
	}

	Request build() {
		return new Request(this);
	}

	/**
	 * build http request, and send out
	 */
	public HttpResponse send(String req) {
		okhttp3.MediaType contentType = getContentType();
		return new HttpResponse(execute(RequestBody.create(contentType, req)));
	}

	/**
	 * 支持javaBean转form（不能有嵌套类型，fieldName =String.valueOf(fieldValue)） Map转Form
	 * get请求拼接到url后，?k=v&k=v post请求放入body content-type为json时直接JSON.toString
	 * 
	 * @param req
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public HttpResponse send(Object req) {
		okhttp3.MediaType contentType = getContentType();
		String reqBody = null;

		if (HttpConstants.FORM_SUBTYPE.equalsIgnoreCase(contentType.subtype())) {
			if (!(req instanceof Map)) {
				buildForm(BeanMap.create(req));
			} else {
				reqBody = buildForm((Map<String, Object>) req);
			}
		} else if (HttpConstants.JSON_SUBTYPE.equalsIgnoreCase(contentType.subtype())) {
			reqBody = JSON.toJSONString(req);
		}
		if (method == HttpMethod.GET) {
			this.url = this.url + "?" + reqBody;
		}
		return new HttpResponse(execute(RequestBody.create(contentType, reqBody)));
	}

	public HttpResponse send(byte[] req) {
		okhttp3.MediaType contentType = getContentType();
		return new HttpResponse(execute(RequestBody.create(contentType, req)));
	}

	private String buildForm(Map<String, Object> req) {

		if (MapUtils.isEmpty(req)) {
			return null;
		}
		StringBuilder sb = new StringBuilder();
		for (Entry<String, ?> e : req.entrySet()) {
			sb.append(e.getKey()).append("=").append(e.getValue() == null ? "" : e.getValue()).append("&");
		}
		sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}

	private Response execute(RequestBody body) {
		okhttp3.Request.Builder builder = new okhttp3.Request.Builder().url(url).method(method.name(), body);
		headers.forEach((name, value) -> {
			builder.addHeader(name, value);
		});
		try {
			return this.okHttpClient.newCall(builder.build()).execute();
		} catch (Exception e) {
			throw new HttpRequestException(e);
		}
	}

	private okhttp3.MediaType getContentType() {
		String contentType = headers.get(HttpConstants.HEADER_CONTENT_TYPE);
		if (StringUtils.isBlank(contentType)) {
			headers.put(HttpConstants.HEADER_CONTENT_TYPE, this.contentType);
		}else{
			this.contentType = contentType;
		}
		return okhttp3.MediaType.parse(this.contentType);
	}

}